#include "XWindow.ph"
#ifdef __GNUG__
#pragma implementation
#endif

#include "XWindowPort.h"

#include "Class.h"
#include "String.h"
#include "XFont.h"
#include "XBitmap.h"
#include "Window.h"
#include "Manager.h"
#include "Look.h"
#include "Timer.h"
#include "System.h"
#include "XColorMap.h"
#include "ET_stdio.h"

extern bool   gWinDebug;
extern int    gArgc;
extern char **gArgv;

static XWindow mainwindow;
static bool down;

const u_long Msk1= ButtonPressMask | ButtonReleaseMask |
					PropertyChangeMask |
					OwnerGrabButtonMask |
					PointerMotionMask | PointerMotionHintMask |
					EnterWindowMask | LeaveWindowMask;
const u_long dfltMsk= Msk1 |
					StructureNotifyMask | ExposureMask |
					KeyPressMask | FocusChangeMask;
const u_long ptrMsk= Msk1;

static Pixmap MakePixmap(XDisplay *dpy, Pixmap &pm, Point &oldsize,
			const Point &newsize)
{
	if (pm == None || newsize.x > oldsize.x || newsize.y > oldsize.y) {
		if (pm)
			XFreePixmap(dpy, pm);
		oldsize= newsize;
		pm= XWinSystem::CreatePixmap(oldsize, gDepth);
	}
	return pm;
}

#define SetTS() if (solid != FillSolid) XSetTSOrigin(dpy,gc,origin.x,origin.y)

//---- window creation/destruction ---------------------------------------------

NewMetaImpl0(XWindowPort,WindowPort);

XWindowPort::XWindowPort(Window *bw, WindowType wt, GrCursor curs)
												: WindowPort(bw, wt, curs)
{
	XSetWindowAttributes setWAttr;
	long flgs= 0;

	dpy= XWinSystem::display;

	bool managed= TRUE;
	bool transient= FALSE;
	bool backingstore= FALSE;
	//bool header= TRUE;
	//bool closebox= TRUE;
	bool resize= FALSE;
	//bool pin= TRUE;
	bool dialog= FALSE;
	overlay= FALSE;

	switch (wt) {
	case eWinPlain:
		resize= TRUE;
		break;
	case eWinStandard:
		resize= TRUE;
		break;
	case eWinDialog:
		//transient= TRUE;
		dialog= TRUE;
		break;
	case eWinPulldownMenu:
	case eWinPopupMenu:
		overlay= TRUE;
		transient= TRUE;
		managed= FALSE;
		break;
	case eWinIcon:
		break;
	}
	func= -1;
	icon= FALSE;
	currpsz= -1;
	solid= FillSolid;
	depth= XDefaultDepth(dpy, XWinSystem::screen);

	if (wcmap == 0) {
		wcmap= WindowSystem::wcmap;
		wcmap->Ref();
	}

/*
	flgs|= CWColormap;
	setWAttr.colormap= cmap;
*/

	flgs|= CWBackPixmap;
	setWAttr.background_pixmap= None;

	flgs|= CWBorderPixmap;
	setWAttr.border_pixmap= None;
/*
	flgs|= CWBackPixel;
	setWAttr.background_pixel= 0;

	flgs|= CWBorderPixel;
	setWAttr.border_pixel= 0;
*/
	flgs|= CWCursor;
	setWAttr.cursor= XWinSystem::UseCursor(cursor);

	if (transient) {
		flgs|= CWSaveUnder;
		setWAttr.save_under= XTrue;
	}

	if (backingstore) {
		flgs|= CWBackingStore;
		setWAttr.backing_store= WhenMapped;
	}

	if (!managed) {
		flgs|= CWOverrideRedirect;
		setWAttr.override_redirect= XTrue;
	}

	flgs|= CWEventMask;
	setWAttr.event_mask= dfltMsk;

	flgs|= CWDontPropagate;
	setWAttr.do_not_propagate_mask= KeyPressMask | KeyReleaseMask |
		ButtonPressMask | ButtonReleaseMask | PointerMotionMask;

	id= winid= XCreateWindow(dpy, XWinSystem::rootwin,
						0, 0, 1, 1, 0, GetDepth(), InputOutput,
								(Visual*) CopyFromParent, flgs, &setWAttr);
	if (mainwindow == 0)
		mainwindow= winid;
		
	XGrabButton(dpy, (int)AnyButton, AnyModifier, winid, XFalse,
		(u_int) ptrMsk, GrabModeSync, GrabModeAsync, None, None);

	gc= XWinSystem::CreateGC(winid, 0, NULL);

	if (managed) {
		int wmprot= 0;
		Atom WMProtocols[3];
		//if (cTakeFocus.Exists())
		//    WMProtocols[wmprot++]= cTakeFocus.XA();
		//if (cSaveYourself.Exists())
		//   WMProtocols[wmprot++]= cSaveYourself.XA();
		if (cDeleteWindow.Exists())
			WMProtocols[wmprot++]= cDeleteWindow.XA();
		XSetWMProtocols(dpy, winid, WMProtocols, wmprot);

		// mark this window as an ET++ window
		char *commwin= form("%d", ((XWinSystem*)gWindowSystem)->awin);
		XChangeProperty(dpy, winid, cIsEtWindow.XA(), XA_STRING,
						8, PropModeAppend, (byte*)commwin, strlen(commwin)+1);

		XWMHints *wmhints= XAllocWMHints();

		wmhints->flags|= InputHint;
		wmhints->input= XTrue;

		wmhints->flags|= StateHint;
		wmhints->initial_state= NormalState;

		wmhints->flags|= WindowGroupHint;
		wmhints->window_group= GetLeader();
		
		XClassHint *classhints= XAllocClassHint();
		classhints->res_name= (char*) gProgname;
		classhints->res_class= "ET++";
		
		XSetWMProperties(dpy, winid, 0, 0, gArgv, gArgc, 0, wmhints, classhints);
		XFree((char*) wmhints);
		XFree((char*) classhints);

		if (transient)
			XSetTransientForHint(dpy, winid, mainwindow);
		if (cOpenLookWinAttr.Exists()) {
			long props[5];
			props[0]= props[1]= props[2]= props[3]= props[4]= 0;
			props[0] |= 1;
			if (dialog) {
				props[0] |= 4;  // pin state
				props[1]= cOpenLookWinCmd.XA();
				props[3]= 1;
			} else
				props[1]= cOpenLookWinBase.XA();
			XChangeProperty(dpy, winid, cOpenLookWinAttr.XA(),
				cOpenLookWinAttr.XA(), 32, PropModeReplace, (byte*) props, 5);
		}
		if (cOpenLookDecorAdd.Exists() && resize) {
			Atom props[5];
			props[0]= cOpenLookResize.XA();
			XChangeProperty(dpy, winid, cOpenLookDecorAdd.XA(),
						XA_ATOM, 32, PropModeReplace, (byte*) props, 1);
		}
		if (cOpenLookDecorDel.Exists() && !resize) {
			Atom props[5];
			props[0]= cOpenLookResize.XA();
			XChangeProperty(dpy, winid, cOpenLookDecorDel.XA(),
				XA_ATOM, 32, PropModeReplace, (byte*) props, 1);
		}
	}
}

XWindow XWindowPort::GetLeader()
{
	if (win) {
		Manager *m= (Manager*) win->GetNextHandler();
		if (m) {
			Manager *nm= (Manager*) m->GetNextHandler();
			if (nm) {
				Window *w= nm->GetWindow();
				if (w)  {
					XWindowPort *xw= (XWindowPort*) w->GetPortDesc();
					if (xw) {
						return xw->winid;
					}
				}
			}
		}
	}
	return winid;
}

XWindowPort::~XWindowPort()
{
	if (winid) {
		XFreeGC(dpy, gc);
		XDestroyWindow(dpy, winid);
		if (sizehints) {
			XFree((char*) sizehints);
			sizehints= 0;
		}
		winid= 0;
	}
}

int XWindowPort::DevGetId()
{
	return winid;
}

//---- Patterns & Rasterops ----------------------------------------------------

void XWindowPort::DevSetPattern(Bitmap *b)
{
	DevBitmap *bm= b->GetDevBitmap();
	XBitmap *xbm= (XBitmap*) bm->PrepareBitmap(this, bm->Size(), b->GetColorMapper());
	SetFunction(GXcopy);
	if (b->MaxPixelValue() > 1) {
		SetFillStyle(FillTiled);
		XSetTile(dpy, gc, xbm->GetPixmap());
	} else {
		SetFillStyle(FillOpaqueStippled);
		XSetStipple(dpy, gc, xbm->GetPixmap());
		XSetForeground(dpy, gc, XWinSystem::black);
		XSetBackground(dpy, gc, XWinSystem::white);
	}
}

void XWindowPort::DevSetColor2(u_long pixel)
{
	FlushColorMap();
	SetFunction(GXcopy);
	SetFillStyle(FillSolid);
	XSetForeground(dpy, gc, pixel);
}

void XWindowPort::DevSetOther(int pid)
{
	SetFillStyle(FillSolid);
	switch (pid) {
	case -1:
		SetFunction(GXinvert);
		break;
	case 1:
		SetFunction(GXcopy);
		XSetForeground(dpy, gc, XWinSystem::white);
		break;
	case 2:
		SetFunction(GXcopy);
		XSetForeground(dpy, gc, XWinSystem::black);
		break;
	}
}

void XWindowPort::setFunction(int f)
{
	func= f;
	if (func == GXinvert) {
		XSetPlaneMask(dpy, gc, XWinSystem::white ^ XWinSystem::black);
		XSetFunction(dpy, gc, GXinvert);
	} else {
		XSetPlaneMask(dpy, gc, AllPlanes);
		XSetFunction(dpy, gc, func);
	}
}

void XWindowPort::setLine(int psz)
{
	currpsz= psz;
	if (psz == 1 && solid == FillSolid)
		psz= 0;
	XSetLineAttributes(dpy, gc, psz, LineSolid, CapButt, JoinMiter);
}

//---- Cursors -----------------------------------------------------------------

void XWindowPort::DevSetCursor(GrCursor c)
{
	if (winid) {
		XDefineCursor(dpy, winid, XWinSystem::UseCursor(c));
		XFlush(dpy);
	}
}

//---- Event mapping -----------------------------------------------------------

static void StateToFlags(Token *t, u_int st)
{
	if (st & ShiftMask)
		t->Flags|= eFlgShiftKey;
	if (st & ControlMask)
		t->Flags|= eFlgCntlKey;
	if (st & Mod1Mask)
		t->Flags|= eFlgMetaKey;
}

Point XWindowPort::GetMyOrigin()
{
	int x, y;
	XWindow child;

	if (XTranslateCoordinates(dpy, winid,
									XWinSystem::rootwin, 0, 0, &x, &y, &child))
		return Point(x, y);
	fprintf(stderr, "GetMyOrigin: XTranslateCoordinates failed\n");
	return gPoint0;
}

Point XWindowPort::GetParentOrigin()
{
	Point orig;
	XWindow w, child, root, parent;

	XGrabServer(dpy);
	for (w= winid; ; w= parent) {
		XWindow *children= 0;
		u_int nchildren;
		if (XQueryTree(dpy, w, &root, &parent, &children,
															&nchildren) == 0) {
			fprintf(stderr, "XQueryTree failed\n");
			break;
		}
		if (parent == root)
			break;
		if (children)
			XFree((char*)children);
	}

	if (XTranslateCoordinates(dpy, w,
					XWinSystem::rootwin, 0, 0, &orig.x, &orig.y, &child) == 0)
		fprintf(stderr, "XTranslateCoordinates failed\n");
	XUngrabServer(dpy);
	return orig;
}

bool XWindowPort::MapEvent(XEvent &xe, Token *tp)
{
	Rectangle newrect;
	Token t;
	char buffer[40];
	int keysym, ccnt, bufsize= 40;
	KeySym _keysym;
	XComposeStatus compose;
	char *thebuf= 0;
	int thelen= 0;

	if (xe.xany.window != winid) {
		fprintf(stderr, "XWindowPort::MapEvent: wrong event\n");
		return FALSE;
	}

	FlushColorMap();

	switch (xe.type) {
	case NoExpose:
		return FALSE;
		
	case GraphicsExpose:
	case Expose:
		for (;;) {
			newrect= Rectangle(xe.xexpose.x, xe.xexpose.y, xe.xexpose.width,
														xe.xexpose.height);
			if (xe.xexpose.count <= 0)
				break;
			InvalidateRect(newrect);

			XCheckWindowEvent(dpy, winid, ExposureMask, &xe);
		}

		if (configure) {
			OpenNotify(portrect);
			configure= FALSE;
		} else
			InvalidateRect(newrect, TRUE);      // last exposure in sequence
		return FALSE;

	case ConfigureNotify:
		if (portrect.extent.x != xe.xconfigure.width ||
									portrect.extent.y != xe.xconfigure.height)
			configure= TRUE;
		portrect.origin= GetParentOrigin();
		portrect.extent= Point(xe.xconfigure.width, xe.xconfigure.height);
		return FALSE;

	case MapNotify:
		configure= TRUE;
		return FALSE;

	case UnmapNotify:
		CloseNotify();
		return FALSE;
		
	case MappingNotify:
		XRefreshKeyboardMapping(&xe.xmapping);
		return FALSE;

	case KeyPress:
		ccnt= XLookupString(&xe.xkey, buffer, bufsize, &_keysym, &compose);
		keysym= (int) _keysym;
		
		if (keysym >= XK_KP_0 && keysym <= XK_KP_9)
			keysym-= (XK_KP_0 - XK_0);
		
		switch ((int)keysym) {
		case XK_KP_Space:   t.Code= ' ';            break;
		case XK_KP_Tab:     t.Code= '\t';           break;
		case XK_KP_Enter:   t.Code= '\n';           break;
		case XK_KP_Equal:   t.Code= '=';            break;
		case XK_KP_Multiply:t.Code= '*';            break;
		case XK_KP_Add:     t.Code= '+';            break;
		case XK_KP_Separator:t.Code= ',';           break;
		case XK_KP_Subtract:t.Code= '-';            break;
		case XK_KP_Decimal: t.Code= '.';            break;
		case XK_KP_Divide:  t.Code= '/';            break;

		case XK_Left:       t.Code= eEvtCursorLeft; break;
		case XK_Up:         t.Code= eEvtCursorUp;   break;
		case XK_Right:      t.Code= eEvtCursorRight;break;
		case XK_Down:       t.Code= eEvtCursorDown; break;
		case XK_Tab:        t.Code= '\t';           break;
		case XK_Delete:     t.Code= '\b';           break;
		case XK_BackSpace:  t.Code= '\b';           break;
		case XK_Linefeed:   t.Code= '\n';           break;
		case XK_Return:     t.Code= '\r';           break;

		default:
			if (keysym > 0 && keysym < 256)
				t.Code= keysym;
			else if (keysym >= XK_F1 && keysym <= XK_F35)
				t.Code= (short) (eEvtPfkFirst + (keysym - XK_F1) + 1);
			else
				return FALSE;
			break;
		}
		StateToFlags(&t, xe.xkey.state);
		if (t.Flags & eFlgCntlKey) {
			if (t.Flags & eFlgShiftKey)
				t.Code-= 0x40;
			else
				t.Code-= 0x60;
		}
		if (t.Flags & eFlgMetaKey)
			t.Code+= 0x80;
		t.Pos= Point(xe.xkey.x, xe.xkey.y);
		t.At= xe.xkey.time;
		break;
		
	case ButtonPress:
		t.Code= eEvtButtons + xe.xbutton.button - 1;
		StateToFlags(&t, xe.xbutton.state);
		t.At= xe.xbutton.time;
		t.Pos= Point(xe.xbutton.x, xe.xbutton.y);
		XWinSystem::allow= TRUE;
		down= TRUE;
		if (gWinDebug)
			fprintf(stderr, "Down: %d\n", winid);
		break;
		
	case ButtonRelease:
		t.Code= eEvtButtons + xe.xbutton.button - 1;
		StateToFlags(&t, xe.xbutton.state);
		t.At= xe.xbutton.time;
		t.Pos= Point(xe.xbutton.x, xe.xbutton.y);
		t.Flags|= eFlgButDown;
		XUngrabPointer(dpy, CurrentTime);
		XWinSystem::allow= TRUE;
		down= FALSE;
		if (gWinDebug)
			fprintf(stderr, "Up: %d\n", winid);
		break;
		
	case MotionNotify:
		if (xe.xmotion.is_hint) {
			while(XCheckTypedEvent(dpy, MotionNotify, &xe))
				;   // overread MotionNotifies
			t.Pos= getMousePos(xe.xmotion.state);
			if (t.Pos == lastpos)
				return FALSE;
		} else
			t.Pos= Point(xe.xmotion.x, xe.xmotion.y);
		t.Code= eEvtLocMove;
		t.At= xe.xmotion.time;
		StateToFlags(&t, xe.xmotion.state);
		break;
		
	case LeaveNotify:
	case EnterNotify:
		if (xe.xcrossing.focus && (xe.xcrossing.detail == NotifyAncestor
				|| xe.xcrossing.detail == NotifyInferior
				|| xe.xcrossing.detail == NotifyNonlinear)) {
			active= (xe.type == EnterNotify);
			if (win)
				win->FocusNotify(active || focus);
		}
		return FALSE;

	case FocusIn:
	case FocusOut:
		if (xe.xfocus.detail == NotifyAncestor
				|| xe.xfocus.detail == NotifyInferior
				|| xe.xfocus.detail == NotifyNonlinear) {
			focus= (xe.type == FocusIn);
			if (win)
				win->FocusNotify(active || focus);
		}
		return FALSE;

	case ClientMessage:
		if (cTakeFocus.Exists() && xe.xclient.data.l[0] == cTakeFocus.XA()) {
				fprintf(stderr, "TakeFocus\n");
			//XSetInputFocus(dpy, winid, RevertToParent, WindowSystem::lasttime);
			if (winid)
				XSetInputFocus(dpy, winid, RevertToParent, CurrentTime);
		} else if (cSaveYourself.Exists() && xe.xclient.data.l[0] == cSaveYourself.XA()) {
			XSetCommand(dpy, mainwindow, gArgv, gArgc);
		} else if (cDeleteWindow.Exists() && xe.xclient.data.l[0] == cDeleteWindow.XA()) {
			if (win)
				win->Control(cCLOSE, cPartAction, 0);
		} else if (cXViewDragLoad.Exists()
							&& xe.xclient.message_type == cXViewDragLoad.XA()) {
			Atom actual_type_return;
			int actual_format_return;
			u_long nitems_return;
			u_long len, bytes_after_return;
			u_char *prop_return= 0;
		
			XGetWindowProperty(dpy,  xe.xclient.data.l[3],
					xe.xclient.data.l[4], 0L, 0L, XFalse,
				AnyPropertyType, &actual_type_return, &actual_format_return,
					&nitems_return, &bytes_after_return, &prop_return);

			len= bytes_after_return;

			XGetWindowProperty(dpy, xe.xclient.data.l[3],
					xe.xclient.data.l[4], 0L, (len-1)/4+1, XTrue,
				AnyPropertyType, &actual_type_return, &actual_format_return,
					&nitems_return, &bytes_after_return, &prop_return);
		
			if (win)
				win->ExtCommand(0, "load", (char*)prop_return, (int)len, thebuf, thelen);
			XFree((char*)prop_return);
		} else if (cDecWmMoved.Exists() && xe.xclient.message_type == cDecWmMoved.XA()) {
			// Window has been moved by DECWindows window manager
			// get new window origin in screen coordinates
			portrect.origin= GetParentOrigin();
		} else {
			char *p= XGetAtomName(dpy, xe.xclient.message_type);
			fprintf(stderr, "XWindowPort: Clientmessage: %s\n", p);
			XFree(p);
		}
		return FALSE;

	default:
		return FALSE;
	}

	if (tp)
		*tp= t;
	return TRUE;
}

//---- window management -------------------------------------------------------

void XWindowPort::UpdateHints(const Point &o, const Point &e)
{
	if (!overlay) {
		Point ee;
		if (sizehints == 0)
			sizehints= XAllocSizeHints();
		sizehints->flags= 0;
		
		if (win)
			ee= win->GetMinSize().extent;
		else
			ee= e;
		if (sizehints->x != o.x || sizehints->y != o.y
			|| sizehints->width != e.x || sizehints->height != e.y
				|| sizehints->min_width != ee.x || sizehints->min_height != ee.y) {
				
			sizehints->flags|= USPosition;
			sizehints->x= o.x;
			sizehints->y= o.y;

			sizehints->flags|= USSize;
			sizehints->width= e.x;
			sizehints->height= e.y;

			sizehints->flags|= PMinSize;
			sizehints->min_width= ee.x;
			sizehints->min_height= ee.y;
			XSetWMNormalHints(dpy, winid, sizehints);
		}
	}
}

void XWindowPort::DevShow(WindowPort *fp, const Rectangle &rr)
{
	Rectangle r(rr);
	XEvent xe;
	Token t;

	father= (XWindowPort*) fp;

	if (father) {
		Point off(father->GetMyOrigin()-father->GetParentOrigin());
		// fprintf(stderr, "father: %d %d\n", off.x, off.y);
		r.origin+= off;
	}

	UpdateHints(r.origin, r.extent);

	if (portrect != r)
		XMoveResizeWindow(dpy, winid, r.origin.x, r.origin.y, r.extent.x, r.extent.y);
	XMapRaised(dpy, winid);

	while (state != eWsShown) {
		XWindowEvent(dpy, winid, StructureNotifyMask | ExposureMask, &xe);
		MapEvent(xe, &t);
	}

	if (father && overlay) {
		while (XCheckTypedWindowEvent(dpy, father->winid,
								Expose, &xe) != 0 && xe.xexpose.count > 0)
			;
	}

	if (father && down) {
		XGrabPointer(dpy, winid, XFalse, (u_int)ptrMsk,
			GrabModeAsync, GrabModeAsync, None, None, WindowSystem::lasttime);
	}
}

void XWindowPort::DevHide()
{
	if (overlay)
		XUnmapWindow(dpy, winid);
	else
		XWithdrawWindow(dpy, winid, XWinSystem::screen);

	while (state == eWsShown) {
		XEvent xe;
		Token t;
		XWindowEvent(dpy, winid, StructureNotifyMask | ExposureMask, &xe);
		MapEvent(xe, &t);
	}

	if (father && down) {
		XGrabPointer(dpy, father->winid, XFalse, (u_int)ptrMsk,
			GrabModeAsync, GrabModeAsync, None, None, WindowSystem::lasttime);
	}
}

void XWindowPort::DevIconize()
{
	XIconifyWindow(dpy, winid, 0);
}

void XWindowPort::DevTop(bool top)
{
	if (top)
		XRaiseWindow(dpy, winid);
	else
		XLowerWindow(dpy, winid);
}

void XWindowPort::DevSetExtent(const Point &e)
{
	UpdateHints(portrect.origin, e);
	if (portrect.extent != e)
		XResizeWindow(dpy, winid, e.x, e.y);
}

void XWindowPort::DevSetOrigin(const Point &o)
{
	UpdateHints(o, portrect.extent);
	if (portrect.origin != o)
		XMoveWindow(dpy, winid, o.x, o.y);
}

//---- graphical primitives ----------------------------------------------------

void XWindowPort::CopyArea(XWindow from, XWindow to, const Point &o,
										const Point &e, const Point &d)
{
	SetFunction(GXcopy);
	XCopyArea(dpy, from, to, gc, o.x, o.y, e.x, e.y, d.x, d.y);
}

void XWindowPort::DevClip(const Rectangle &r)
{
	if (WindowSystem::fullscreen) {
		XSetClipMask(dpy, gc, None);
	} else {
		XRectangle xcr;
		xcr.x= r.origin.x;
		xcr.y= r.origin.y;
		xcr.width= r.extent.x;
		xcr.height= r.extent.y;
		XSetClipRectangles(dpy, gc, 0, 0, &xcr, 1, YXBanded);
	}
}

void XWindowPort::DevStrokeLine2(int psz, const Rectangle&, GrLineCap,
		const Point &start, const Point &end)
{
	if (psz < 0)
		return;
	SetLine(psz);
	SetTS();
	XDrawLine(dpy, id, gc, start.x, start.y, end.x, end.y);
}

static void AdjustRect(int psz, Rectangle &r)
{
	if (psz > 1) {
		r.origin+= psz/2;
		r.extent-= psz;
	} else
		r.extent-= 1;
}

void XWindowPort::DevStrokeRect2(int psz, const Rectangle &rr)
{
	Rectangle r(rr);
	if (psz < 0)
		return;
	SetLine(psz);
	AdjustRect(psz, r);
	SetTS();
	XDrawRectangle(dpy, id, gc, r.origin.x, r.origin.y, r.extent.x, r.extent.y);
}

void XWindowPort::DevFillRect(const Rectangle &r)
{
	SetTS();
	XFillRectangle(dpy, id, gc, r.origin.x, r.origin.y, r.extent.x, r.extent.y);
}

void XWindowPort::DevStrokeRRect2(int psz, const Rectangle &rr, const Point &dia)
{
	XArc a[4];
	XSegment s[4];

	if (psz < 0)
		return;

	Rectangle r(rr);
	AdjustRect(psz, r);
	Point o(r.origin);
	Point e(r.extent-dia);

	for (int i= 0; i < 4; i++) {
		a[i].angle1= (i*90) << 6;
		a[i].angle2= 90 << 6;
		a[i].width= dia.x;
		a[i].height= dia.y;
		a[i].x= o.x;
		a[i].y= o.y;
	}
	a[0].x+= e.x;
	a[3].x+= e.x;
	a[2].y+= e.y;
	a[3].y+= e.y;

	s[0].x1= s[3].x1= o.x+dia.x/2;
	s[0].x2= s[3].x2= o.x+r.extent.x-dia.x/2;
	s[1].x1= s[1].x2= o.x;
	s[2].x1= s[2].x2= o.x+r.extent.x;

	s[0].y1= s[0].y2= o.y;
	s[1].y1= s[2].y1= o.y+dia.y/2;
	s[1].y2= s[2].y2= o.y+r.extent.y-dia.y/2;
	s[3].y1= s[3].y2= o.y+r.extent.y;

	SetLine(psz);
	SetTS();
	XDrawArcs(dpy, id, gc, a, 4);
	XDrawSegments(dpy, id, gc, s, 4);
}

void XWindowPort::DevFillRRect2(const Rectangle &r, const Point &dia)
{
	XArc a[4];
	XRectangle rr[3];
	Point o(r.origin);
	Point e(r.extent-dia);

	for (int i= 0; i < 4; i++) {
		a[i].angle1= (i*90) << 6;
		a[i].angle2= 90 << 6;
		a[i].width= dia.x;
		a[i].height= dia.y;
		a[i].x= o.x;
		a[i].y= o.y;
	}
	a[0].x+= e.x;
	a[3].x+= e.x;
	a[2].y+= e.y;
	a[3].y+= e.y;

	rr[0].x= rr[2].x= o.x + dia.x/2;
	rr[1].x= o.x;

	rr[0].y= o.y;
	rr[1].y= o.y+dia.y/2;
	rr[2].y= o.y+r.extent.y-dia.y/2;

	rr[0].width= rr[2].width= e.x;
	rr[1].width= r.extent.x;

	rr[0].height= rr[2].height= dia.y/2;
	rr[1].height= e.y;

	SetTS();
	XFillArcs(dpy, id, gc, a, 4);
	XFillRectangles(dpy, id, gc, rr, 3);
}

void XWindowPort::DevStrokeOval2(int psz, const Rectangle &rr)
{
	if (psz < 0)
		return;
	SetLine(psz);
	Rectangle r(rr);
	AdjustRect(psz, r);
	SetTS();
	XDrawArc(dpy, id, gc, r.origin.x, r.origin.y,
					r.extent.x, r.extent.y, 0, 360*64);
}

void XWindowPort::DevFillOval2(const Rectangle &r)
{
	SetTS();
	XFillArc(dpy, id, gc, r.origin.x, r.origin.y,
					r.extent.x, r.extent.y, 0, 360*64);
}

void XWindowPort::DevStrokeWedge2(int psz, GrLineCap, const Rectangle &rr,
							    int s, int len)
{
	if (psz < 0)
		return;
	SetLine(psz);
	Rectangle r(rr);
	AdjustRect(psz, r);
	SetTS();
	XDrawArc(dpy, id, gc, r.origin.x, r.origin.y,
			r.extent.x, r.extent.y, ((450-s) % 360)*64, -len*64);
}

void XWindowPort::DevFillWedge2(const Rectangle &r, int s, int len)
{
	SetTS();
	XFillArc(dpy, id, gc, r.origin.x, r.origin.y,
			r.extent.x, r.extent.y, ((450-s) % 360)*64, -len*64);
}

void XWindowPort::DevStrokePolygon2(const Rectangle &r, Point *pts, int npts,
						GrPolyType, int psz, GrLineCap)
{
	if (psz < 0)
		return;
	register int i, x= r.origin.x, y= r.origin.y;
	XPoint *xp, *xpp;
	xp= xpp= (XPoint*) Alloca(npts * sizeof(XPoint));

	SetLine(psz);
	for (i= 0; i < npts; i++, xpp++, pts++) {
		xpp->x= pts->x + x;
		xpp->y= pts->y + y;
	}
	SetTS();
	XDrawLines(dpy, id, gc, xp, npts, CoordModeOrigin);
	Freea(xp);
}

void XWindowPort::DevFillPolygon2(const Rectangle &r, Point *pts, int npts,
			GrPolyType)
{
	register int i, x= r.origin.x, y= r.origin.y;
	XPoint *xp, *xpp;
	xp= xpp= (XPoint*) Alloca(npts * sizeof(XPoint));

	for (i= 0; i < npts; i++, xpp++, pts++) {
		xpp->x= pts->x + x;
		xpp->y= pts->y + y;
	}
	SetTS();
	XFillPolygon(dpy, id, gc, xp, npts,
			(npts <= 3) ? Convex : Complex, CoordModeOrigin);
	Freea(xp);
}

void XWindowPort::DevShowBitmap(const Rectangle &r, Bitmap *bm)
{
	DevBitmap *dbm= bm->GetDevBitmap();
	XBitmap *x= (XBitmap*) dbm->PrepareBitmap(this, r.extent, bm->GetColorMapper());
	Pixmap pm= x->GetPixmap();
	XBitmap *mask= (XBitmap*) dbm->GetMask(r.extent, bm->GetColorMapper());

	if (mask) {
		XSetForeground(dpy, gc, 0x00000000);
		XSetBackground(dpy, gc, 0xffffffff);
		SetFunction(GXand);
		XSetGraphicsExposures(dpy, gc, XFalse);
		XCopyPlane(dpy, mask->GetPixmap(), id, gc, 0, 0,
				r.extent.x, r.extent.y, r.origin.x, r.origin.y, 0x00000001);
		SetFunction(GXor);
		XCopyArea(dpy, pm, id, gc, 0, 0,
				r.extent.x, r.extent.y, r.origin.x, r.origin.y);
} else {
		if (dbm->depth > 1) {
			CopyArea(pm, id, gPoint0, r.extent, r.origin);
		} else {
			XSetStipple(dpy, gc, pm);
			XSetTSOrigin(dpy, gc, r.origin.x, r.origin.y);
			SetFillStyle(FillStippled);
			XFillRectangle(dpy, id, gc, r.origin.x, r.origin.y,
													r.extent.x, r.extent.y);
		}
	}
}

//---- text batching -----------------------------------------------------------

static XTextItem ti[100], *tip= ti;
static byte tbc[400], *tbp= tbc;
static Font *lastfont;

void XWindowPort::DevShowChar(Font *fdp, const Point &gap, Rune c,
														const Rectangle&)
{
	if (gap.y != 0 || tip-ti >= 100-1 || tbp-tbc >= MaxTextBatchCnt) // X cannot handle multiple lines !!
		FlushAnyText();

	if (tbp == tbc) {   // new batch
		tip= ti;
		tip->delta= 0;
		tip->chars= (char*)tbp;
		tip->nchars= 0;
		tip->font= ((XServerFont*)fdp)->GetId();
		lastfont= fdp;
	} else if (fdp != lastfont || gap.x != 0) {   // switch to next
		tip++;
		tip->delta= gap.x;
		tip->chars= (char*)tbp;
		tip->nchars= 0;
		if (fdp != lastfont)
			tip->font= ((XServerFont*)fdp)->GetId();
		else
			tip->font= (XFont) None;
		lastfont= fdp;
	}

	*tbp++= c;
	tip->nchars++;
}

void XWindowPort::DevShowTextBatch(const Rectangle&, const Point &pos)
{
	if (tbp > tbc) {
		if (tip-ti == 0) {
			XSetFont(dpy, gc, tip->font);
			XDrawString(dpy, id, gc, pos.x, pos.y, tip->chars, tip->nchars);
		} else
			XDrawText(dpy, id, gc, pos.x, pos.y, ti, tip-ti+1);
		tbp= tbc;
	}
}

//---- scrolling ---------------------------------------------------------------

void XWindowPort::DevScrollRect(const Rectangle &r, const Point &delta)
{
	XEvent ev;
	XSetGraphicsExposures(dpy, gc, XTrue);
	CopyArea(winid, winid, r.origin - delta, r.extent, r.origin);
	do {
		XWindowEvent(dpy, winid, ExposureMask, &ev);
		MapEvent(ev);
	} while (ev.type == GraphicsExpose && ev.xgraphicsexpose.count > 0);
	XSetGraphicsExposures(dpy, gc, XFalse);
}

//---- input -------------------------------------------------------------------

Point XWindowPort::getMousePos(u_int &st)
{
	XWindow rootW, childW;
	int x0, y0, wx, wy;

	XQueryPointer(dpy, winid, &rootW, &childW, &x0, &y0, &wx, &wy, &st);
	return Point(wx, wy);
}

void XWindowPort::DevSetMousePos(const Point &p, bool m)
{
	XWarpPointer(dpy, None, m ? (XWindow)None : winid, 0, 0, 0, 0, p.x, p.y);
}

void XWindowPort::DevFullscreen(bool mode)
{
	if (mode) {
		XWindow w;
		XTranslateCoordinates(dpy, winid,
					XWinSystem::rootwin, 0, 0, &fso.x, &fso.y, &w);
		id= XWinSystem::rootwin;
		XGrabServer(dpy);
		XSetSubwindowMode(dpy, gc, IncludeInferiors);
		XSetClipMask(dpy, gc, None);
	} else {
		id= winid;
		XSetSubwindowMode(dpy, gc, ClipByChildren);
		XUngrabServer(dpy);
		XFlush(dpy);
		fso= gPoint0;
	}
}

//---- double buffering --------------------------------------------------------

static Point batchSize;
static Rectangle batchRect;
static bool inBatch;

void XWindowPort::DevGiveHint(int code, int, void *vp)
{
	switch (code) {
	case eHintBatch:
		if (!WindowSystem::batch || overlay || (id != winid))
			return;
		inBatch= TRUE;
		batchRect= *((Rectangle*)vp);
		id= MakePixmap(dpy, XWinSystem::batchPixmap, batchSize, batchRect.origin+batchRect.extent);
		break;
		
	case eHintUnbatch:
		if (!WindowSystem::batch || !inBatch)
			return;
		inBatch= FALSE;
		id= winid;
		CopyArea(XWinSystem::batchPixmap, winid, batchRect.origin,
											batchRect.extent, batchRect.origin);
		break;

	case eHintWinBusy:
		if (winid)
			XWinSystem::SetBusy(winid);
		break;
		
	case eHintCacheStart:
		{
			batchInfo *bi= (batchInfo*) vp;
			((XBitmap*)bi->b)->GetPixmap();
			CopyArea(((XBitmap*)bi->b)->pm, id, bi->p, bi->r.extent, bi->r.origin);
		}
		break;

	case eHintCacheEnd:
		{
			batchInfo *bi= (batchInfo*) vp;
			MakePixmap(dpy, ((XBitmap*)bi->b)->pm, ((XBitmap*)bi->b)->size, bi->r.extent);
			CopyArea(id, ((XBitmap*)bi->b)->pm, bi->r.origin, bi->r.extent, gPoint0);
		}
		break;
	}
}

void XWindowPort::DevSetTitle(const char *t)
{
	if (winid && !overlay) {
		XTextProperty tp;
		XStringListToTextProperty((char**) &t, 1, &tp);
		if (isicon)
			XSetWMIconName(dpy, winid, &tp);
		else
			XSetWMName(dpy, winid, &tp);
	}
}

